<template>
  <div style="border: 1px solid #e4e4e4">
    <div
      id="input"
      class="hashtag-textarea"
    >
      <div
        id="input-overlay"
        class="hashtag-textarea__overlay"
        :style="overlayStyle"
      />
      <div
        id="input-true-text"
        ref="mTextarea"
        class="hashtag-textarea__true-text"
        wrap="soft"
        :style="trueTextStyle"
        contentEditable="true"
        spellcheck="false"
        @paste="optimizePasteEvent"
        @click="inputClick"
        @input="doInput"
        @keydown.enter.prevent="enter"
      ></div>

      <div
        v-show="shouldShowPlaceholder"
        class="hashtag-textarea__placeholder"
      >
        {{ option.placeholder }}
      </div>
    </div>
    <div class="bottom-container">
      <el-button
        v-if="
          accountType === DYNAMIC_TYPE.OFFICAL ||
          detailType === DYNAMIC_TYPE.OFFICAL
        "
        type="text"
        @click="selectTopic"
      >
        #添加话题
      </el-button>
      <div class="limit-num">{{ curLength }}/{{ maxlength }}</div>
    </div>
  </div>
</template>

<script>
import { DYNAMIC_TYPE } from '@/const/type';
export default {
  name: 'HashtagTextarea',
  props: {
    option: {
      type: Object,
    },
    maxlength: {
      type: Number,
      default: 99999,
    },
    accountType: {
      type: String,
    },
    detailType: {
      type: String,
    },
  },
  data() {
    return {
      DYNAMIC_TYPE,
      hashtagList: [],
      shouldShowPlaceholder: true,
      defaultOption: {
        defaultContent: '',
        textColor: '#333333',
        font: '14px',
        hashtagBackgroundColor: 'transparent',
        hashtagColor: '#419EFF',
        placeholder: '请输入',
        isEditMode: true,
      },
      focusedHashtagNode: {},
      curLength: 0,
    };
  },
  computed: {
    isSafariBrowser: function () {
      let isSafariBrowser = false;
      const userAgent = window.navigator.userAgent.toLowerCase();

      if (
        userAgent.indexOf('safari') !== -1 &&
        userAgent.indexOf('chrome') === -1
      ) {
        isSafariBrowser = true;
      }

      return isSafariBrowser;
    },
    trueTextStyle: function () {
      const style = {};
      style.font = this.option.font;
      style['z-index'] = this.option.isEditMode ? 2 : -2;
      return style;
    },
    overlayStyle: function () {
      const style = {};
      style.color = this.option.textColor;
      style.font = this.option.font;

      return style;
    },
    hashtagStyle: function () {
      let style = 'style="';
      style += 'color:' + this.option.hashtagColor + ';';
      style += 'background:' + this.option.hashtagBackgroundColor + ';';

      if (!this.option.isEditMode) {
        style += 'cursor: default;';
      }
      style += '"';

      return style;
    },
    regExp: function () {
      return /#([^#]+)#/g;
    },
  },
  watch: {
    hashtagList: function (newVal, oldVal) {
      const isEqualVal = JSON.stringify(newVal) === JSON.stringify(oldVal);

      if (!isEqualVal) {
        const hashtagData = {};
        if (newVal === null) {
          hashtagData.target = '';
        } else if (oldVal === null) {
          // NOTE: Set focused hashtag node
          const hashtagTarget = this.getHashtagTargetNode(0);
          hashtagData.target = hashtagTarget.innerText;
          this.focusedHashtagNode = hashtagTarget;
        } else {
          if (newVal.length < oldVal.length) {
            hashtagData.target = '';
          } else {
            // NOTE: Set focused hashtag node
            const diff = this.getDiffArrayWithIndex(newVal, oldVal);
            const index =
              diff.diffValue === undefined ? newVal.length - 1 : diff.index;
            const hashtagTarget = this.getHashtagTargetNode(index);
            hashtagData.target = hashtagTarget.innerText;
            this.focusedHashtagNode = hashtagTarget;
          }
        }

        hashtagData.hashtags = newVal;
        this.$emit('onChangeHashtag', hashtagData);
      }
    },
  },
  created() {
    for (const key in this.defaultOption) {
      const value = this.option[key];
      if (value === undefined) {
        this.option[key] = this.defaultOption[key];
      }
    }
  },
  mounted() {
    // NOTE: Create Observer. This is called when text content of the target is changed
    const target = document.getElementById('input-true-text');
    const observer = new MutationObserver(this.onObserveElement);
    const config = {
      childList: true,
      characterData: true,
      characterDataOldValue: true,
      subtree: true,
    };

    observer.observe(target, config);

    // NOTE: Display initial content if exist
    target.innerText = this.option.defaultContent;

    const overlayElm = document.getElementById('input-overlay');
    overlayElm.addEventListener('click', this.onSelectHashtag, false);
  },
  destroyed() {
    const overlayElm = document.getElementById('input-overlay');
    overlayElm.removeEventListener('click', this.onSelectHashtag, false);
  },
  methods: {
    enter(event) {
      event.stopPropagation();
      return false;
    },
    selectTopic() {
      this.$emit('selectTopic');
    },
    //点击输入框
    inputClick() {
      const srcContent = this.getInputContent();
      this.$emit(
        'doInput',
        srcContent,
        '',
        '',
        this.getCursorPosition(),
        srcContent.length
      );
    },
    //输入监听
    doInput(event) {
      let srcContent = this.getInputContent();
      if (srcContent.length >= this.maxlength) {
        srcContent = srcContent.substring(0, this.maxlength);
        this.replaceHashtagNodeContent(srcContent);
        this.setFocus();
      }
      this.$emit(
        'doInput',
        srcContent,
        event.inputType,
        event.data,
        this.getCursorPosition(),
        srcContent.length
      );
      // this.replaceContent()
    },
    getCursorPosition() {
      const element = document.getElementById('input-true-text');
      let caretOffset = 0;
      const doc = element.ownerDocument || element.document;
      const win = doc.defaultView || doc.parentWindow;
      const sel = win.getSelection();
      if (sel.rangeCount > 0) {
        const range = win.getSelection().getRangeAt(0);
        const preCaretRange = range.cloneRange();
        preCaretRange.selectNodeContents(element);
        preCaretRange.setEnd(range.endContainer, range.endOffset);
        caretOffset = preCaretRange.toString().length;
      }
      return caretOffset;
    },

    setFocus() {
      this.mTextarea = this.$refs.mTextarea;
      const selection = window.getSelection();
      const range = document.createRange();
      range.selectNodeContents(this.mTextarea);
      range.collapse(false);
      selection.removeAllRanges();
      selection.addRange(range);
    },
    getInputContent() {
      const target = document.getElementById('input-true-text');

      // NOTE: Need to convert html character with escapecharacter
      const content = this.escapeHtml(target.innerText);
      // NOTE: Trim line break \n
      const spaceExp = /^\n\n/gm;
      const content2 = content.replace(spaceExp, (_) => {
        return '\n';
      });
      let srcContent = this.isSafariBrowser ? content : content2;
      if (srcContent.length >= this.maxlength) {
        srcContent = srcContent.substring(0, this.maxlength);
      }
      return srcContent;
    },

    optimizePasteEvent(e) {
      // 监听粘贴内容到输入框事件，对内容进行处理 处理掉复制的样式标签，只拿取文本部分
      e.stopPropagation();
      e.preventDefault();
      let text = '';
      const event = e.originalEvent || e;
      if (event.clipboardData && event.clipboardData.getData) {
        text = event.clipboardData.getData('text/plain');
      } else if (window.clipboardData && window.clipboardData.getData) {
        text = window.clipboardData.getData('text');
      }
      if (document.queryCommandSupported('insertText')) {
        document.execCommand('insertText', false, text);
      } else {
        document.execCommand('paste', false, text);
      }
    },

    onObserveElement(mutations) {
      mutations.forEach((mutation) => {
        const type = mutation.type;

        switch (type) {
          // NOTE: The textContent will be changed in the line, this type is called.
          case 'characterData':
            this.replaceContent();
            break;

          // NOTE: The line break will be occured, this type is called.
          case 'childList':
            this.replaceContent();
            break;

          default:
            break;
        }
      });

      // NOTE: Show / hide placeholder.
      const target = document.getElementById('input-true-text');
      this.shouldShowPlaceholder =
        target.innerHTML === '' || target.innerHTML === '<br>';
    },
    replaceContent() {
      const target = document.getElementById('input-true-text');

      // NOTE: Need to convert html character with escapecharacter
      const content = this.escapeHtml(target.innerText);
      const contentHTML = target.textContent;

      // NOTE: Trim line break \n
      const spaceExp = /^\n\n/gm;
      const content2 = content.replace(spaceExp, function (match) {
        return '\n';
      });
      const srcContent = this.isSafariBrowser ? content : content2;
      const self = this;
      this.curLength = srcContent.length;
      const replaceContent = srcContent.replace(this.regExp, function (match) {
        const idStr = ' id=' + self.getUniqueStr();
        const result = '<i ' + self.hashtagStyle + idStr + '>' + match + '</i>';
        return result;
      });

      // NOTE: Apply content into overlay field.
      const insertNode = document.getElementById('input-overlay');
      insertNode.innerHTML = replaceContent;

      this.hashtagList = this.getHashtagList(content);
    },
    escapeHtml(content) {
      const escapeHashMap = {
        '&': '&amp;',
        '"': '&quot;',
        '<': '&lt;',
        '>': '&gt;',
      };
      return content.replace(/[&"<>]/g, function (match) {
        return escapeHashMap[match];
      });
    },
    getHashtagList(value) {
      const result = value.match(this.regExp);
      return result;
    },
    getHashtagNodeList() {
      const target = document.getElementById('input-overlay');
      const nodes = [];
      const childNodes = target.childNodes;
      childNodes.forEach((child) => {
        nodes.push(child);
      });

      return nodes;
    },
    getDiffArrayWithIndex(newVal, oldVal) {
      const result = {};

      for (let i = 0; i < newVal.length; i++) {
        if (oldVal.indexOf(newVal[i]) === -1) {
          result.diffValue = newVal[i];
          result.index = i;
          break;
        }
      }

      return result;
    },
    onSelectHashtag(e) {
      const target = e.target;
      const tagName = target.tagName;
      if (tagName === 'I') {
        const content = target.textContent;
        this.$emit('onSelectHashtag', target);
      }
    },
    replaceHashtagNodeContent(newValue) {
      // if (!newValue) return;
      this.focusedHashtagNode.textContent = newValue;
      const trueTarget = document.getElementById('input-true-text');
      // const overlayTarget = document.getElementById('input-overlay');
      this.curLength = newValue.length;
      trueTarget.innerText = newValue;
    },
    getUniqueStr() {
      var strong = 1000;
      return (
        new Date().getTime().toString(16) +
        Math.floor(strong * Math.random()).toString(16)
      );
    },
    getHashtagTargetNode(index) {
      const target = document.getElementById('input-overlay');
      const nodes = target.childNodes;
      let count = 0;
      let hashtagTarget = {};

      for (let i = 0; i < nodes.length; i++) {
        const node = nodes[i];
        if (node.tagName === 'I') {
          if (index === count) {
            hashtagTarget = node;
            break;
          }

          count++;
        }
      }
      return hashtagTarget;
    },
  },
};
</script>

<style lang="less">
@textWidth: 100%;
@textPadding: 10px;

// NOTE: Component of hashtag textarea
.hashtag-textarea {
  position: relative;
  margin: @textPadding;
  overflow: auto;
  background: white;
  min-height: 140px;
  height: 100%;
  width: 98%;

  // NOTE: Textarea layout. Fix position
  &__true-text {
    border: 0;
    background: transparent;
    position: absolute;
    top: 0;
    color: transparent;
    caret-color: black;
    outline: none;
  }

  &__overlay {
    -webkit-tap-highlight-color: transparent;
  }

  &__overlay,
  &__true-text {
    height: 100%;
    width: @textWidth;
    white-space: pre-wrap;
    overflow-wrap: break-word;
    -webkit-user-select: auto;
    font-style: normal;
    line-height: 1.1;
  }

  &__placeholder {
    font: 14px;
    position: absolute;
    line-height: 1.1;
    top: 0;
    color: lightgray;
  }
}
.bottom-container {
  display: flex;
  flex-direction: row;
  margin: 0 10px;
  justify-content: space-between;
}
.limit-num {
  color: #999;
  font-size: 12px;
  flex: 1;
  text-align: end;
}
</style>
